package com.example.demo.template;

import com.example.demo.abs.AbstractCodeGenParser;
import com.example.demo.util.FileUtil;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Mybatis模板
 */
public class MybatisTemplate {
    /**
     * 文件编码，包括输入和输出的文件
     */
    private String encoding = "UTF-8";
    /**
     * 模板文件转义字符<br/>
     * 两个转义字符间的字符串将通过解析器的对应方法来解析，连续的两个转义字符代表一个转义字符的内容。
     */
    private String templateSpecialChar = "$";
    /**
     * 参数文件中组内参数的分隔符
     */
    private String paramSplitString = " ";
    /**
     * 模板文件的路径<br/>
     * 路径为目录则取目录中的所有文件为模板
     */
    private String templatePath = "MybatisCodeGenParser/template";
    /**
     * 参数文件的路径
     */
    private String parameterFile = "param.ini";
    /**
     * 解析器的类名
     */
    private String parserClassName;
    /**
     * 目标文件的存放位置
     */
    private String desDir = "target";

    /**
     * 存放读入的参数信息
     */
    private List<String[]> paramList = new ArrayList<String[]>();
    /**
     * 存放读入的模板信息
     */
    private Map<String, String> templates = new HashMap<String, String>();
    /**
     * 模板文件的匹配模式
     */
    private Pattern pattern;

    /**
     * 创建一个模板引擎实例
     */
    public MybatisTemplate() {
    }

    /**
     * 创建一个模板引擎实例
     * @param templatePath
     * @param parameterFile
     * @param parserClassName
     * @param desDir
     */
    public MybatisTemplate(String templatePath, String parameterFile, String parserClassName, String desDir) {
        this.templatePath = templatePath;
        this.parameterFile = parameterFile;
        this.parserClassName = parserClassName;
        this.desDir = desDir;
    }

    /**
     * 根据配置生产文件
     * @throws IOException
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SecurityException
     * @throws NoSuchMethodException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public void generate() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
        if (parserClassName == null) {
            throw new RuntimeException("parserClassName should not be null!");
        }
        Class<?> parserClass = Class.forName(parserClassName);
        if (!AbstractCodeGenParser.class.isAssignableFrom(parserClass)) {
            throw new RuntimeException("parser's class should inherit class \"prv.jjh.engin.AbstractParser\"!");
        }

        readTemplates();
        readParameters();
        pattern = Pattern.compile(Pattern.quote(templateSpecialChar) + ".*?" + Pattern.quote(templateSpecialChar));

        for (int i = 0; i < paramList.size(); i++) {
            AbstractCodeGenParser parser = (AbstractCodeGenParser) parserClass.newInstance();
            String[] params = paramList.get(i);
            parser.setParams(params);
            String _fileName = parser._fileName();
            for (Entry<String, String> template : templates.entrySet()) {
                String templateName = template.getKey();
                String desFileName = templateName.replace(templateSpecialChar, _fileName);
                String templateContent = template.getValue();

                File dir = FileUtil.parseFile(desDir);
                FileUtil.forceMkdir(dir);
                FileUtil.writeStringToFile(new File(dir, desFileName), parseContent(parser, templateContent), encoding);
            }
        }
    }

    /**
     * 根据解析器解析内容
     * @param parser
     * @param content
     * @return
     * @throws SecurityException
     * @throws NoSuchMethodException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private String parseContent(AbstractCodeGenParser parser, String content) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        StringBuffer sb = new StringBuffer();
        Matcher m = pattern.matcher(content);
        while (m.find()) {
            String token = m.group();
            if (token.equals(templateSpecialChar + templateSpecialChar)) {
                m.appendReplacement(sb, templateSpecialChar.replace("\\", "\\\\").replace("$", "\\$"));
            } else {
                String methodName = token.substring(1, token.length() - 1);
                Method method = parser.getClass().getMethod(methodName, new Class[]{});
                String value = (String) method.invoke(parser, new Object[]{});
                m.appendReplacement(sb, value.replace("\\", "\\\\").replace("$", "\\$"));
            }
        }
        m.appendTail(sb);
        return sb.toString();
    }

    /**
     * 读入模板
     * @throws IOException
     */
    private void readTemplates() throws IOException {
        File template = FileUtil.parseFile(templatePath);
        if (!template.exists()) {
            throw new RuntimeException("template can not be found!");
        }

        if (template.isFile()) {
            readTemplateFile(template);
        } else {
            File[] templateFiles = template.listFiles();
            for (File file : templateFiles) {
                readTemplateFile(file);
            }
        }
    }

    /**
     * 读入一个模板文件
     * @param file
     * @throws IOException
     */
    private void readTemplateFile(File file) throws IOException {
        String name = file.getName();
        String content = FileUtil.readFileToString(file, encoding);
        templates.put(name, content);
    }

    /**
     * 读入参数
     * @throws IOException
     */
    private void readParameters() throws IOException {
        File parameter = FileUtil.parseFile(parameterFile);
        if (!parameter.exists()) {
            throw new RuntimeException("parameter file can not be found!");
        }

        if (parameter.isDirectory()) {
            throw new RuntimeException("parameter file must be a file!");
        }

        List<String> list = FileUtil.readLines(parameter, encoding);
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if (!"".equals(s.trim())) {
                paramList.add(s.split(Pattern.quote(paramSplitString), 3));
            }
        }
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public String getTemplatePath() {
        return templatePath;
    }

    public void setTemplatePath(String templatePath) {
        this.templatePath = templatePath;
    }

    public String getParameterFile() {
        return parameterFile;
    }

    public void setParameterFile(String parameterFile) {
        this.parameterFile = parameterFile;
    }

    public String getParserClassName() {
        return parserClassName;
    }

    public void setParserClassName(String parserClassName) {
        this.parserClassName = parserClassName;
    }

    public String getDesDir() {
        return desDir;
    }

    public void setDesDir(String desDir) {
        this.desDir = desDir;
    }

    public String getParamSplitString() {
        return paramSplitString;
    }

    public void setParamSplitString(String paramSplitString) {
        this.paramSplitString = paramSplitString;
    }

    public String getTemplateSpecialChar() {
        return templateSpecialChar;
    }

    public void setTemplateSpecialChar(String templateSpecialChar) {
        this.templateSpecialChar = templateSpecialChar;
    }

}
